#include "move_component.h"

#include <set>

#include "map_grid.h"
#include "map_system.h"

static const ZNumber kCheckCastleDistance = 2 * 6;

MoveComponent::MoveComponent(MapSystem* map_system, Entity* entity)
    : map_system_(map_system),
      entity_(entity),
      component_type_(MSEntityComponentType::kEntityComponentMove),
      move_status_(kMoveStatusNone),
      pre_check_time_(0),
      is_notice_(false),
      loop_move_end_check_(false),
      path_find_flag_(0),
      pre_check_castle_pos_(-1, -1) {}

void MoveComponent::Clear() {
  move_status_ = MoveStatus::kMoveStatusNone;
  target_position_.Clear();
  cur_region_paths_.clear();
  pre_check_castle_pos_.Set(-1, -1);
}

bool MoveComponent::MoveTo(const Vector2D& pos,
                           std::function<void(bool, EStopMoveReason)>&& cb) {
  Clear();

  // if (entity_->GetMapSystem()->CheckStatus(pos, kMapGridStatusObstacle)) {
  //   return false;
  // }

  target_position_ = pos;
  move_call_back_ = cb;

  move_status_ = kMoveStatusPathFind;
  FindPath(GetEntityPosition(), pos);
  return true;
}

void MoveComponent::SetEntityPosition(const Vector2D& position) const {
  entity_->SetPosition(position);
}

void MoveComponent::SetEntityDirection(const Vector2D& direction) const {
  entity_->SetDirection(direction);
}

const Vector2D& MoveComponent::GetEntityDirection() const {
  return entity_->GetDirection();
}

const Vector2D& MoveComponent::GetEntityPosition() const {
  return entity_->GetPosition();
}

bool MoveComponent::FindPath(Vector2D start, Vector2D end) {
  ++path_find_flag_;
  DoFindPath(start, end, path_find_flag_);
  return false;
}

void MoveComponent::SetSpeed(const ZNumber& speed) { move_speed_ = speed; }

void MoveComponent::OnStopMove(bool is_done, EStopMoveReason reason) {
  ++path_find_flag_;

  if (move_call_back_) {
    move_call_back_(is_done, reason);
  }

  Clear();
}

bool MoveComponent::IsEmplyMoveTarget() {
  return cur_region_paths_.empty() && move_cricle_paths_.empty();
}

Vector2D* MoveComponent::GetPathMoveTarget() {
  if (cur_region_paths_.empty()) {
    return nullptr;
  }

  auto& state_path = cur_region_paths_.front();
  assert(!state_path.paths.empty());
  return &state_path.paths.front();
}

Vector2D* MoveComponent::GetMoveTarget() {
  if (IsEmplyMoveTarget()) {
    return nullptr;
  }

  if (!move_cricle_paths_.empty()) {
    return &move_cricle_paths_.front();
  }

  if (cur_region_paths_.empty()) {
    return nullptr;
  }

  auto& state_path = cur_region_paths_.front();

  assert(!state_path.paths.empty());
  return &state_path.paths.front();
}

bool MoveComponent::PopCurMoveTarget() {
  if (cur_region_paths_.empty()) {
    return true;
  }

  auto& state_path = cur_region_paths_.front();
  assert(!state_path.paths.empty());

  state_path.paths.pop_front();

  if (!state_path.paths.empty()) {
    return true;
  }

  cur_region_paths_.pop_front();

  pre_check_castle_pos_.SetRealValue(0, 0);

  if (cur_region_paths_.empty()) {
    return true;
  }
  return true;
}

bool MoveComponent::PopMoveTarget() {
  assert(!IsEmplyMoveTarget());
  if (!move_cricle_paths_.empty()) {
    move_cricle_paths_.pop_front();
    if (move_cricle_paths_.empty() && move_status_ == kMoveStatusMoveCricle) {
      move_status_ = kMoveStatusMoving;
    }
    return true;
  }
  auto res = PopCurMoveTarget();
  if (!res) {
    return false;
  }
  return true;
}

bool MoveComponent::UpdateMovePos(ZNumber& time_pass) {
  ZNumber cur_speed = GetCurSpeed();
  if (cur_speed == 0) {
    OnStopMove(false, kStopMoveReasonZeroSpeed);
    return false;
  }

  bool notice_pop_path = false;
  auto cur_pos = GetEntityPosition();
  auto direction = GetEntityDirection();
  bool is_update = false;
  bool is_stop = false;

  while (time_pass > 0 && !IsEmplyMoveTarget()) {
    auto get_res = GetMoveTarget();
    if (!get_res) {
      OnStopMove(false, kStopMoveReasonUnknown);
      is_stop = true;
      break;
    }

    auto target_pos = *get_res;
    is_update = true;

    auto v = target_pos - cur_pos;
    if (!v.IsZero()) direction = v;

    ZNumber total_time_pass = v.Length() / cur_speed;
    if (total_time_pass <= time_pass) {
      time_pass -= total_time_pass;
      cur_pos = target_pos;
      auto pop_res = PopMoveTarget();
      if (!pop_res) {
        OnStopMove(false, kStopMoveReasonUnknown);
        is_stop = true;
        break;
      }

      if (pop_res) {
        auto path_res = GetMoveTarget();
        if (path_res) {
        }
      }
      continue;
    } else {
      cur_pos = cur_pos + v.Normal() * time_pass * cur_speed;
      time_pass.SetValue(0);
      break;
    }
  }

  if (is_update) {
    SetEntityPosition(cur_pos);
    SetEntityDirection(direction);
  }

  if (!is_stop && IsEmplyMoveTarget()) {
    OnStopMove(true, kStopMoveReasonSuccess);
  }
  return false;
}

const ZNumber kOffsetSize(0.1f);

struct SortEntityData {
  EntityPtr entity;
  Vector2D cvt;
};

class SortRelationEntityFunc {
 public:
  explicit SortRelationEntityFunc(const Vector2D& check_pos)
      : check_pos_(check_pos) {}

  bool operator()(const SortEntityData& first, const SortEntityData& second) {
    return first.cvt.LengthSquare() < second.cvt.LengthSquare();
  }

 private:
  Vector2D check_pos_;
};

void MoveComponent::CheckPreCastle() {
  if (move_status_ != kMoveStatusMoving || cur_region_paths_.empty() ||
      !move_cricle_paths_.empty()) {
    return;
  }

  std::cout << "CheckPreCastle" << std::endl;
  auto cur_pos = GetEntityPosition();

  Vector2D* target_pos_ptr = GetPathMoveTarget();
  if (!target_pos_ptr) {
    return;
  }

  auto target_pos = *target_pos_ptr;
  auto v = target_pos - cur_pos;
  Vector2D check_pos = target_pos;
  if (v.LengthSquare() > kCheckCastleDistance * kCheckCastleDistance) {
    check_pos = cur_pos + v.Normal() * kCheckCastleDistance;
  }

  if (pre_check_castle_pos_.X() > 0) {
    auto dest = pre_check_castle_pos_ - check_pos;
    if (dest.LengthSquare() <= kCheckCastleDistance * kCheckCastleDistance) {
      return;
    }
  }

  pre_check_castle_pos_ = check_pos;
  std::list<uint32_t> grid_indexs;
  std::cout << check_pos.Print() << std::endl;
  map_system_->LineInterGrid(cur_pos, check_pos, grid_indexs);
  // for (const auto& grid : grid_indexs) {
  //   std::cout << "grid:" << grid << std::endl;
  // }

  std::set<uint64_t> checked_entities;
  std::list<SortEntityData> relation_entities;
  for (auto& grid_index : grid_indexs) {
    std::cout << "grid_index:" << grid_index << std::endl;
    auto grid = map_system_->GetGrid(grid_index);
    if (!grid) {
      std::cout << "test2" << std::endl;
      continue;
    }

    auto& entities = grid->GetEntities();
    for (auto entity : entities) {
      if (entity->GetEntityType() != EntityType::kEntityType_Town) {
        //std::cout << "test3:" << static_cast<int>(entity->GetEntityType()) << " " << grid_index << std::endl;
        continue;
      }

      if (checked_entities.find(entity->GetEntityId()) !=
          checked_entities.end()) {
        continue;
      }

      auto cl = entity->GetCollision();
      if (cl <= 0) {
                std::cout << "test1" << std::endl;
        continue;
      }

      static Vector2D cvt;
      if (!map_system_->IsRelationEntity(cur_pos, target_pos,
                                         entity->GetCenterPosition(),
                                         cl - kOffsetSize, cvt)) {
        std::cout << "test" << std::endl;
        continue;
      }

      // std::cout << "cur_pos:" << cur_pos.Print() << std::endl;
      // std::cout << "target_pos:" << target_pos.Print() << std::endl;
      // std::cout << "cvt:" << cvt.Print() << std::endl;

      checked_entities.insert(entity->GetEntityId());
      relation_entities.push_back({std::move(entity), cvt});
    }
  }

  if (relation_entities.empty()) {
    return;
  }

  relation_entities.sort(SortRelationEntityFunc(cur_pos));
  auto start_pos = cur_pos;
  move_cricle_paths_.clear();
  std::list<Vector2D> move_cricle_paths;
  for (auto& check_entity : relation_entities)
  {
    auto cl = check_entity.entity->GetCollision() + entity_->GetCollision();
    if (cl > check_entity.entity->GetScale())
    {
      cl = check_entity.entity->GetScale();
    }

    std::cout << "start_pos:" << start_pos.Print() << std::endl;
    std::cout << "target_pos:" << target_pos.Print() << std::endl;
    std::cout << "cl:" << cl.GetValue() << std::endl;
    std::cout << "center_pos:" << check_entity.entity->GetCenterPosition().Print() << std::endl;

    auto result = map_system_->CaculateMoveCriclePath(start_pos, target_pos,
      check_entity.entity->GetCenterPosition(), cl, move_cricle_paths);
    
    for (const auto &path : move_cricle_paths) {
      std::cout << "move_cricle_path:" << path.Print() << std::endl;
    }
  }

}

void MoveComponent::HandleMove() {
  int64_t cur_time = std::chrono::duration_cast<std::chrono::milliseconds>(
                         std::chrono::system_clock::now().time_since_epoch())
                         .count();
  int64_t time_pass = cur_time - pre_check_time_;
  pre_check_time_ = cur_time;
  if (time_pass < 0) {
    OnStopMove(false, kStopMoveReasonUnknown);
    return;
  }

  ZNumber time;
  time.SetValue(time_pass);
  CheckPreCastle();
  if (time > 0 && (move_status_ == kMoveStatusMoving ||
                   move_status_ == kMoveStatusMoveCricle)) {
    UpdateMovePos(time);
  }
}

void MoveComponent::StartMove() {
  std::cout << "StartMove" << std::endl;
  move_status_ = MoveStatus::kMoveStatusMoving;
  pre_check_time_ = std::chrono::duration_cast<std::chrono::milliseconds>(
                        std::chrono::system_clock::now().time_since_epoch())
                        .count();
  HandleMove();
}

void MoveComponent::InitPathFindData(
    const std::list<Vector2D>& find_path_list) {
  move_path_data_.clear();
  if (!move_cricle_paths_.empty()) {
    move_path_data_.push_back(GetEntityPosition());
  }

  for (const auto& cricle_path : move_cricle_paths_) {
    move_path_data_.push_back(cricle_path);
  }

  cur_region_paths_.clear();

  StatePathData path_data;
  path_data.pass_id = ++g_pass_id;

  for (const auto& path : find_path_list) {
    path_data.paths.push_back(path);
  }

  cur_region_paths_.push_back(std::move(path_data));

  if (!cur_region_paths_.empty()) {
    if (!cur_region_paths_.front().paths.empty()) {
      target_position_ = cur_region_paths_.front().paths.front();
    }
  }
}

void MoveComponent::OnPathFindResult(
    const std::list<Vector2D>& find_path_list) {
  InitPathFindData(find_path_list);

  if (move_status_ == kMoveStatusPathFind) {
    StartMove();
  }
}

void MoveComponent::DoFindPath(Vector2D start, Vector2D end,
                               int64_t path_find_flag) {
  std::list<Vector2D> find_path_list;
  find_path_list.push_back(end);
  OnPathFindResult(find_path_list);
}